package com.leco.exam.webcommon.advice;

import com.leco.exam.common.biz.errorcode.ErrorCode;
import com.leco.exam.common.biz.exception.ExamException;
import com.leco.exam.webcommon.common.BaseExceptionHandler;
import com.leco.exam.webcommon.common.Response;
import com.leco.exam.webcommon.common.ResponseData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler extends BaseExceptionHandler {
    private Map<Class, ErrorCode> exceptionMap = new HashMap();

    @PostConstruct
    public void init() {
        exceptionMap.put(HttpMessageNotReadableException.class, ErrorCode.SYS_PARAM_INVALID);
        exceptionMap.put(IllegalArgumentException.class, ErrorCode.SYS_PARAM_INVALID);
        exceptionMap.put(HttpRequestMethodNotSupportedException.class, ErrorCode.SYS_METHOD_NOT_SUPPORT);
        exceptionMap.put(NoHandlerFoundException.class, ErrorCode.NOT_FOUND_CODE);
        exceptionMap.put(MethodArgumentTypeMismatchException.class, ErrorCode.BAD_REQUEST);
        exceptionMap.put(MissingServletRequestParameterException.class, ErrorCode.BAD_REQUEST);
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseData<Object> defaultErrorHandler(HttpServletRequest req, Exception e) {
        ResponseData<Object> errorData = errorData(e);
        if (needErrorLog(errorData.getCode())) {
            exceptionLog(e);
        }
        return errorData;
    }

    private boolean needErrorLog(int code) {
        return code == ErrorCode.SERVER_ERROR_CODE.getCode();
    }

    private ResponseData<Object> errorData(Exception e) {
        if (e instanceof MethodArgumentNotValidException) {
            // 注解验证参数异常
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            BindingResult bindResult = exception.getBindingResult();
            String message = bindResult.getAllErrors().stream().map(s -> s.getDefaultMessage()).collect(Collectors.joining(","));
            return Response.failByParams(message);
        }
        if (e instanceof BindException) {
            // 注解验证参数异常
            BindException exception = (BindException) e;
            BindingResult bindResult = exception.getBindingResult();
            String message = bindResult.getAllErrors().stream().map(s -> s.getDefaultMessage()).collect(Collectors.joining(","));
            return Response.failByParams(message);
        }
        ErrorCode errorCode = exceptionMap.getOrDefault(e.getClass(), ErrorCode.SERVER_ERROR_CODE);
        String message;
        int code = errorCode.getCode();
        if (e instanceof ExamException) {
            code = ((ExamException) e).getCode();
            message = e.getMessage();
        } else {
            if (code > ErrorCode.BAD_REQUEST.getCode() && code <= ErrorCode.BIZ_MIN_CODE) {
                message = errorCode.getMessage();
            } else {
                // 业务错误喷具体含义
                message = e.getMessage();
            }
        }

        exceptionLog(e);

        return Response.fail(message, code);
    }
}
